package spectra.ee.web.account.service;

import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import com.ibatis.sqlmap.client.SqlMapClient;

import spectra.base.cm.model.ConfigProperty;
import spectra.base.licensemanager.SeedAlg;
import spectra.base.licensemanager.Sha256Cipher;
import spectra.base.log.Logger;
import spectra.base.sql.ibatis.SqlMapperWeb;
import spectra.base.util.DateUtil;
import spectra.base.util.PasswordUtil;
import spectra.base.util.StringUtil;
import spectra.ee.EEWebConfig;
import spectra.ee.EEPublic;
import spectra.ee.commons.account.model.PasswordHistory;
import spectra.ee.web.WebPublic;
import spectra.ee.web.account.model.Account;
import spectra.ee.web.account.model.AccountCreationRequest;
import spectra.ee.web.account.model.AccountPropertyValue;
import spectra.ee.web.account.model.NodeAccountRel;
import spectra.ee.web.common.model.IdListForm;

/**
 * 노드 관련 클래스.
 *
 * @author kmhan
 */
public class AccountServiceBase
{

    /** Logger객체. */
    private static Logger logger = Logger.getLoggerInstance(AccountServiceBase.class.getName());

    /** SqlMapClient 객체. */
    protected static SqlMapClient sqlMap = SqlMapperWeb.getSqlMapWebInstance();

    /**
     * 사용자 아이디/비밀번호로 로그인을 체크하는 메소드
     * @param accountId
     * @param password
     * @return
     */
    public Hashtable checkLogin(String domainId, String accountId, String password)
    {
    	Hashtable htSuccess = new Hashtable();
    	try
    	{
    		String message = "";
    		AccountCreationRequest accountCreationRequestObj = (AccountCreationRequest)sqlMap.queryForObject("Account.selectAccountCreationRequestByAccountId", accountId);
    		if (accountCreationRequestObj != null)
    		{
    			// 사용자 등록신청 상태일 경우는 등록대기라는 메세지를 보여준다.
	    		if (WebPublic.APPROVAL_REQUEST.equals(accountCreationRequestObj.getApprovalStatus()))
	    		{
	    			message = "현재 계정은 등록신청 대기중입니다. 관리자에게 문의하시기 바랍니다.";
	    			message = java.net.URLEncoder.encode(message, "UTF-8");
	    			htSuccess.put(WebPublic.RESULT_STRING, WebPublic.FAIL_STRING);
	    			htSuccess.put(WebPublic.MESSAGE_STRING, message);
	    			return htSuccess;
	    		}    		
	    		else if (WebPublic.APPROVAL_REJECT.equals(accountCreationRequestObj.getApprovalStatus())) // 사용자 등록반려일경우에는 등록
	    		{
	    			String rejectContents = StringUtil.nvl(accountCreationRequestObj.getApprovalContents(), "");
	    			message = "현재 계정은 등록신청이 반려되었습니다.";
	    			if (!"".equals(rejectContents))
	    			{
	    				rejectContents = rejectContents.replaceAll("\r", "");
	    				rejectContents = rejectContents.replaceAll("\"", "");
	    				message = message + "\n\n[반려사유]\n" + rejectContents;
	    			}
	    			message = java.net.URLEncoder.encode(message, "UTF-8");
	    			htSuccess.put(WebPublic.RESULT_STRING, WebPublic.FAIL_STRING);
	    			htSuccess.put(WebPublic.MESSAGE_STRING, message);
	    			return htSuccess;
	    		}
    		}
    		
    		// 뷰어 계정일 경우 
    		Account accountObj = (Account)sqlMap.queryForObject("Account.selectByPK", accountId);
    		    		
    		// 계정이 없는 경우
    		if ( accountObj == null || EEPublic.FLAG_Y.equals(accountObj.getDeleteFlag()) )
    		{
    			message = "아이디 또는 비밀번호가 맞지 않습니다.";
    			message = java.net.URLEncoder.encode(message, "UTF-8");
    			htSuccess.put(WebPublic.RESULT_STRING, WebPublic.FAIL_STRING);
    			htSuccess.put(WebPublic.MESSAGE_STRING, message);
                return htSuccess;
    		}
    		
    		//SeedAlg seedAlg = new SeedAlg(EEWebConfig.getEncryptKey().getBytes());
            //String strLoginPassword = seedAlg.encrypt(password);
            Sha256Cipher sha = new Sha256Cipher();
            String strLoginPassword = sha.encrypt(password,null);
            // 비밀번호가 같은경우
            if( accountObj.getPassword().equals(strLoginPassword))
            {
                // 계정이 잠겨져 있을경우
                if ( EEPublic.FLAG_Y.equals(accountObj.getLockFlag()) )
                {
                    message = "입력하신 계정은 현재 잠금상태로 되어있습니다. 관리자에게 문의하십시오.";
                    message = java.net.URLEncoder.encode(message, "UTF-8");
                    htSuccess.put(WebPublic.RESULT_STRING, WebPublic.FAIL_STRING);
                    htSuccess.put(WebPublic.MESSAGE_STRING, message);
                    return htSuccess;
                }
                
            	// 어드민이 아니라면 해당 도메인 연결정보를 체크한다.
            	if (!WebPublic.ROLE_ID_ADMIN.equals(accountObj.getRoleId()))
            	{
	        		String existDomainRel = EEPublic.FLAG_Y; 
	        		NodeAccountRel nodeAccountRelObj = new NodeAccountRel();
	        		if (WebPublic.ROLE_ID_VIEWER.equals(accountObj.getRoleId())) // viewer권한이라면
	        		{
	        			nodeAccountRelObj.setAccountId(accountId);
	            		nodeAccountRelObj.setNodeId(domainId);
	            		nodeAccountRelObj.setModuleType(WebPublic.SERVICE_TYPE_KB);
	        			existDomainRel = (String) sqlMap.queryForObject("Account.existDomainViewerRel", nodeAccountRelObj);
	        		}
	        		else // viewer권한이 아니라면
	        		{
	        			nodeAccountRelObj.setAccountId(accountId);
	            		nodeAccountRelObj.setNodeId(domainId);
	            		nodeAccountRelObj.setModuleType(WebPublic.SERVICE_TYPE_KB);
	        			existDomainRel = (String) sqlMap.queryForObject("Account.existNodeAccountRel", nodeAccountRelObj);
	        		}
	        		if (EEPublic.FLAG_N.equals(existDomainRel))
	        		{
	        			message = "해당 도메인에 로그인 권한이 없습니다. 관리자에게 문의하십시오.";
	        			message = java.net.URLEncoder.encode(message, "UTF-8");
	                	htSuccess.put(WebPublic.RESULT_STRING, WebPublic.FAIL_STRING);
	        			htSuccess.put(WebPublic.MESSAGE_STRING, message);
	                    return htSuccess;
	        		}
            	}
                
            	htSuccess.put(WebPublic.RESULT_STRING, WebPublic.SUCCESS_STRING);
            	htSuccess.put(WebPublic.ACCOUNT_STRING, accountObj);
            	return htSuccess;
            }
            
            message = "아이디 또는 비밀번호가 맞지 않습니다.";
			message = java.net.URLEncoder.encode(message, "UTF-8");
            htSuccess.put(WebPublic.RESULT_STRING, WebPublic.FAIL_STRING);
            htSuccess.put(WebPublic.MESSAGE_STRING, message);
    	}
    	catch (Exception e)
    	{
    		e.printStackTrace();
    		logger.error(e.getMessage());
    	}
    	return htSuccess;
    }
    
    /**
     * 사용자 계정 신청을 하는 메소드
     * @param obj
     * @return
     */
    public boolean insertAccountCreationRequest(AccountCreationRequest obj)
    {
    	boolean result = false;
        try
        {
            sqlMap.insert("Account.insertAccountCreationRequest", obj);
            result = true;
        }
        catch (SQLException e)
        {
            logger.error(e.getMessage());
        }         
        return result;
    }
    
    /**
     * 사용자 아이디로 계정이 존재하는지 체크하는 메소드
     * @param accountId
     * @return
     */
    public boolean checkExistAccountId(String accountId)
    {
    	boolean result = true;
        try
        {        	
        	AccountCreationRequest accountCreationRequestObj = (AccountCreationRequest)sqlMap.queryForObject("Account.selectAccountCreationRequestByAccountId", accountId);
        	// 계정생성 승인대기 상태일때는 신청할 수없음
        	if (accountCreationRequestObj != null && WebPublic.APPROVAL_REQUEST.equals(accountCreationRequestObj.getApprovalStatus()))
        		return true;
        	
        	Account accountObj = (Account) sqlMap.queryForObject("Account.selectAccountAllByAccountId", accountId);
        	if (accountObj != null)
        		result = true;
        	else
        		result = false;
        }
        catch (SQLException e)
        {
            logger.error(e.getMessage());
        }         
        return result;
    }
    
    /**
     * 사용자 아이디로 사용자 정보 가져오는 메소드
     * @param accountId
     * @return
     */
    public Account selectAccountInfo(String accountId)
    {
    	Account accountObj = null;
        try
        {
        	accountObj = (Account)sqlMap.queryForObject("Account.selectByPK", accountId);
        	if (accountObj != null)
        	{
        		// t_account_property_value에서 정보를 가져온다.
        		List accountPropertyValueList = sqlMap.queryForList("Account.selectAccountPropertyValue", accountId);
        		
        		Hashtable htAccountPropertyValue = new Hashtable();
        		if (accountPropertyValueList != null && accountPropertyValueList.size() > 0)
        		{
        			for (int i=0; i<accountPropertyValueList.size(); i++)
        			{
        				AccountPropertyValue accountPropertyValue = (AccountPropertyValue) accountPropertyValueList.get(i);
        				if (accountPropertyValue != null)
        					htAccountPropertyValue.put(accountPropertyValue.getAccountPropertyId(), StringUtil.nvl(accountPropertyValue.getValue(), ""));
        			}
        		}
        		accountObj.setAccountPropertyValue(htAccountPropertyValue);
        	}
        }
        catch (SQLException e)
        {
            logger.error(e.getMessage());
        }         
        return accountObj;
    }
    
    /**
     * 개인정보 수정하는 메소드
     * @param account
     * @return
     */
    public boolean updateAccount(Account account)
    {
    	boolean result = false;
        try
        {
        	sqlMap.startTransaction();
            //SeedAlg seedAlg = new SeedAlg(EEWebConfig.getEncryptKey().getBytes());
            //String strPassword = seedAlg.encrypt(account.getPassword());
            Sha256Cipher sha = new Sha256Cipher();
            String strPassword = sha.encrypt(account.getPassword(),null);
            
        	account.setPassword(strPassword);
        	
            sqlMap.update("Account.updateAccountInfo", account);
            
            account.setCreatedBy(account.getAccountId());
            account.setCreatedDate(DateUtil.getCurrDateTimeStamp());
            sqlMap.insert("Account.insertPasswordHistory", account);
            
            sqlMap.delete("Account.deletePasswordHistoryByPK", account.getAccountId());
            
            Hashtable htAccountPropertyValue = account.getAccountPropertyValue();
            Enumeration en = htAccountPropertyValue.keys();
            while (en.hasMoreElements())
            {
            	String key = (String) en.nextElement();
            	String value = (String) htAccountPropertyValue.get(key);
            	AccountPropertyValue accountPropertyValue = new AccountPropertyValue();
            	accountPropertyValue.setAccountId(account.getAccountId());
            	accountPropertyValue.setAccountPropertyId(key);
            	accountPropertyValue.setValue(value);
            	sqlMap.delete("Account.deleteAccountPropertyValue", accountPropertyValue);
            	sqlMap.insert("Account.insertAccountPropertyValue", accountPropertyValue);
            }
            
            result = true;
            
            sqlMap.commitTransaction();
        }
        catch (SQLException e)
        {
            logger.error(e.getMessage());
        }
        catch (Exception ex)
        {
            logger.error(ex.getMessage());
        }    
        finally
        {
            try
            {
                sqlMap.endTransaction();
            }
            catch(SQLException e)
            {
                logger.error(e.getMessage());
            }
        }
        return result;
    }
    
    /**
     * 입력한 비밀번호가 현재 비밀번호와 동일한지 체크하는 메소드
     * @param account
     * @return
     */
    public boolean checkValidPassword(Account account)
    {
    	boolean bSuccess = false;
        try
        {
        	Account accountObj = (Account)sqlMap.queryForObject("Account.selectByPK", account.getAccountId());

            //SeedAlg seedAlg = new SeedAlg(EEWebConfig.getEncryptKey().getBytes());
            //String strCurrentPassword = seedAlg.encrypt(account.getCurrentPassword());
            
            Sha256Cipher sha = new Sha256Cipher();
            String strCurrentPassword = sha.encrypt(account.getCurrentPassword(),null);
            
        	if (accountObj.getPassword().equals(strCurrentPassword))
        	{
        		bSuccess = true; 
        	}
        }
        catch (SQLException e)
        {
            logger.error(e.getMessage());
        }
        catch (Exception ex)
        {
            logger.error(ex.getMessage());
        }
        return bSuccess;
    }
    
    /**
     * 사용자 아이디로 계정생성정보를 가져오는 메소드
     * @param accountId
     * @return
     */
    public AccountCreationRequest selectAccountCreationRequest(String accountId)
    {
    	AccountCreationRequest obj = null;
    	try
        {
    		obj = (AccountCreationRequest)sqlMap.queryForObject("Account.selectAccountCreationRequestByAccountId", accountId);        
        }
        catch (SQLException e)
        {
            logger.error(e.getMessage());
        }         
        return obj;
    }

    /**
     * 1. 비밀번호 변경시 이전 비밀번호는 사용할 수 없도록.
     * 2. 아이디와 동일한 패스워드는 사용되지 않도록
     * 3. 영문+숫자 최소 10자리 이상 or 영문+숫자+특수문자 포함 8자 이상
     * 4. 연속된 숫자 혹은 영문자가 입력되지 않도록 (예 123456 , abcdef ….)
          : 패스워드 중간에라도 연속되는 문자가 4자이상 포함되면 안됨
     * 5. 동일한 문자열이 3번이상 반복되지 않도록 
     * @param accountId 사용자 아이디
     * @param newPassword 새 비밀번호
     * @return 비밀번호 체크 결과
     */
    public String checkPassword(String accountId, String password)
    {
        String[] arrIdList = new String[]{
                EEPublic.PASSWORD_SECURITY_USE_FLAG, 
                EEPublic.PWD_MIX_USE_FLAG,
                EEPublic.PWD_SEQ_USE_FLAG, EEPublic.PWD_SEQ_COUNT,
                EEPublic.PWD_REPEAT_USE_FLAG, EEPublic.PWD_REPEAT_COUNT,
                EEPublic.PWD_INCLUDE_ID_USE_FLAG, 
                EEPublic.PWD_EQUAL_OLDPWD_USE_FLAG
        };

        IdListForm idList = new IdListForm();
        idList.setRowIdList(arrIdList);
        try
        {
            List propertyList = sqlMap.queryForList("ConfigProperty.selectValueListByPropertyIdList", idList);
    
            ConfigProperty cpValue = null;
            String strSecurityUseFlag = "";
            String strEqualOldPwdUseFlag = "";
            String strIncludeIdUseFlag = "";
            String strMixUseFlag = "";
            String strSeqUseFlag = "";
            String strSeqCount = "";
            String strRepeatUseFlag = "";
            String strRepeatCount = "";
            
            for(Iterator i = propertyList.iterator(); i.hasNext(); )
            {
                cpValue = (ConfigProperty) i.next();
                
                /** 비밀번호 보안 규칙 적용 여부를 t_config_property에서 가져와서 체크함. */
                if (EEPublic.PASSWORD_SECURITY_USE_FLAG.equals(cpValue.getPropertyId()))
                {
                    strSecurityUseFlag = cpValue.getValue();
                }
                
                // 이전 비밀번호와 동일
                if (EEPublic.PWD_EQUAL_OLDPWD_USE_FLAG.equals(cpValue.getPropertyId()))
                {
                    strEqualOldPwdUseFlag = cpValue.getValue();
                }
                
                // 비밀번호가 아이디의 문자열을 포함함.
                if (EEPublic.PWD_INCLUDE_ID_USE_FLAG.equals(cpValue.getPropertyId()))
                {
                    strIncludeIdUseFlag = cpValue.getValue();
                }
                
                // 비밀번호가 영문자+숫자 조합과 길이, 영문자+숫자+특수문자 조합과 길이에 대한 체크
                if (EEPublic.PWD_MIX_USE_FLAG.equals(cpValue.getPropertyId()))
                {
                    strMixUseFlag = cpValue.getValue();
                }
                
                // 주어진 길이 이상으로 연속되는 문자열로 이루어지면 안됨(abcd.., 1234...)
                if (EEPublic.PWD_SEQ_USE_FLAG.equals(cpValue.getPropertyId()))
                {
                    strSeqUseFlag = cpValue.getValue();
                }
                if (EEPublic.PWD_SEQ_COUNT.equals(cpValue.getPropertyId()))
                {
                    strSeqCount = cpValue.getValue();
                }
                
                // 주어진 길이 만큼 동일문자열의 반복이면 안됨.(aaa.., 111..)
                if (EEPublic.PWD_REPEAT_USE_FLAG.equals(cpValue.getPropertyId()))
                {
                    strRepeatUseFlag = cpValue.getValue();
                }
                if (EEPublic.PWD_REPEAT_COUNT.equals(cpValue.getPropertyId()))
                {
                    strRepeatCount = cpValue.getValue();
                }
            }
            
            if( EEPublic.FLAG_Y.equals(strSecurityUseFlag) )
            {           
                // 이전 비밀번호와 동일
                if ( EEPublic.FLAG_N.equals(strEqualOldPwdUseFlag) )
                {
                    List lstPassword = sqlMap.queryForList("Account.selectPasswordHistoryByAccountId", accountId); 
                    if (lstPassword != null)
                    {
                        //SeedAlg seedAlg = new SeedAlg(EEWebConfig.getEncryptKey().getBytes());
                        //String strPassword = seedAlg.encrypt(password);
                        Sha256Cipher sha = new Sha256Cipher();
                        String strPassword = sha.encrypt(password,null);
                        
                        for (int i=0; i<lstPassword.size(); i++)
                        {
                            PasswordHistory passwordHistory = (PasswordHistory) lstPassword.get(i);
                            if( !PasswordUtil.checkByOldPassword(passwordHistory.getPassword(), strPassword) )
                            {
                                return EEPublic.PWD_CHECK_OLDPWD_EQUAL;
                            }
                        }
                    }
                }
                
                // 비밀번호가 아이디의 문자열을 포함함.
                if ( EEPublic.FLAG_N.equals(strIncludeIdUseFlag) )
                {
                    if( !PasswordUtil.checkByAccountId(accountId, password) )
                    {
                        return EEPublic.PWD_CHECK_ID_INCLUDE;
                    }
                }
                
                // 비밀번호가 영문자+숫자 조합과 길이, 영문자+숫자+특수문자 조합과 길이에 대한 체크
                if( EEPublic.FLAG_Y.equals(strMixUseFlag) )
                {
                    if( !PasswordUtil.checkNumberAndAlphabet(password, 10, 8) )
                    {
                        return EEPublic.PWD_CHECK_MIX_ERROR;
                    }
                }
                
                // 주어진 길이 이상으로 연속되는 문자열로 이루어지면 안됨(abcd.., 1234...)
                if ( EEPublic.FLAG_N.equals(strSeqUseFlag) )
                {
                    int iCount = Integer.parseInt(strSeqCount);
                    
                    if( !PasswordUtil.checkSequenceNumberAndAlphabet(password, iCount) )
                    {
                        return EEPublic.PWD_CHECK_SEQ_ERROR+";"+iCount;
                    }
                }           
    
                // 주어진 길이 만큼 동일문자열의 반복이면 안됨.(aaa.., 111..)
                if ( EEPublic.FLAG_N.equals(strRepeatUseFlag) )
                {
                    int iCount = Integer.parseInt(strRepeatCount);
    
                    if( !PasswordUtil.checkRepeatNumberAndAlphabet(password, iCount) )
                    {
                        return EEPublic.PWD_CHECK_REPEAT_ERROR+";"+iCount;
                    }
                }
            }
        } catch(Exception e) {
            e.printStackTrace();
            logger.error(e.getMessage());
        }
        
        return null;
    }
}
